﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Diagnostics;
using System.Reflection;

namespace Ict.DataAccess.Linq
{
    /// <summary>
    /// 支持Lambda表达式的更新器
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Updatable<T> : Modifiable, IQueryable<T>
    {
        public int Update<TElement, TResult>(TElement entity,
            Expression<Func<TElement, TResult>> fields,
            IQueryable<TElement> condition)
        {
            if (!(condition is Selectable<TElement>))
                throw new ArgumentException("Type of condition must be Selectable<TElement>.");
            this.selectClause = fields;
            this.query = (Selectable<TElement>)condition;

            return Update(entity);
        }

        public int Update<TElement, TResult>(TElement entity,
            Expression<Func<TElement, TResult>> fields,
            Expression<Func<TElement, bool>> condition)
        {
            this.selectClause = fields;
            this.WhereClause.Add(condition.Body);

            return Update(entity);
        }

        public int Update<TElement>(TElement entity)
        {
            StringBuilder stringBuilder = new StringBuilder();
            DmlEncoder encoder = DataContext.Current.CreateSqlEncoder();

            stringBuilder.Append("UPDATE ");
            stringBuilder.Append(MapTableAlias<TElement>());
            
            stringBuilder.Append(" SET ");
            BuildSetClause(stringBuilder, encoder, selectClause, entity);
            if (query != null)
            {
                stringBuilder.Append(" FROM ");
                query.BuildQueryList(stringBuilder);
            }
            if (query != null && query.WhereClause != null && query.WhereClause.Count > 0)
            {
                if (whereClause != null)
                    query.WhereClause.AddRange(this.whereClause);
                stringBuilder.Append(" WHERE ");
                query.BuildCondition(stringBuilder);
            }
            else if (whereClause != null)
            {
                stringBuilder.Append(" WHERE ");
                BuildCondition<T>(stringBuilder, whereClause);
            }
            string sql = stringBuilder.ToString();
            Debug.WriteLine(sql);

            int n = 0;
            using (Executor exec = (Provider as ModifiableProvider).Executor)
            {
                n = exec.Update(entity, sql);
            }
            return n;
        }


        private void BuildSetClause<TElement>(StringBuilder stringBuilder, DmlEncoder encoder, 
            Expression expression, 
            TElement entity)
        {
            

            if (expression == null
                || expression.NodeType == ExpressionType.MemberAccess
                && (expression as MemberExpression).Type == this.ElementType)
            {
                IEnumerator<string> istr = TableMapper<T>.TableFieldMap.Values.GetEnumerator();
                Dictionary<string, string>.Enumerator ipair = TableMapper<T>.TableFieldMap.GetEnumerator();
                if (ipair.MoveNext())
                {
                    do
                    {
                        BuildSetValue(stringBuilder, encoder,
                            ipair.Current.Value, entity, ipair.Current.Key);
                        if (!ipair.MoveNext())
                            break;
                        stringBuilder.Append(',');
                    } while (true);
                }
            }
            else if (expression.NodeType == ExpressionType.New)
            {
                IEnumerator<MemberInfo> imember = ((NewExpression)expression).Members.GetEnumerator();
                IEnumerator<Expression> iarg = ((NewExpression)expression).Arguments.GetEnumerator();
                if (imember.MoveNext())
                {
                    iarg.MoveNext();
                    do
                    {
                        string fieldName = TableMapper<TElement>.TableFieldMap[imember.Current.Name];
                        if (fieldName != null)
                        {
                            if (iarg.Current.NodeType == ExpressionType.MemberAccess)
                            {
                                string propertyName = (iarg.Current as MemberExpression).Member.Name;
                                BuildSetValue(stringBuilder, encoder, fieldName, entity, propertyName);
                            }
                            else
                            {
                                BuildSetValue(stringBuilder, encoder, fieldName, entity, iarg.Current);
                            }
                        }
                        if (!imember.MoveNext())
                            break;
                        iarg.MoveNext();
                        if (fieldName != null)
                            stringBuilder.Append(',');
                    } while (true);
                }
            }
            else if (expression.NodeType == ExpressionType.MemberAccess)
            {
                string propertyName = (expression as MemberExpression).Member.Name;
                string fieldName = TableMapper<TElement>.TableFieldMap[propertyName];
                if (fieldName != null)
                {
                    BuildSetValue(stringBuilder, encoder, fieldName, entity, propertyName);
                }
            }
            else if (expression.NodeType == ExpressionType.Lambda)
            {
                BuildSetClause<TElement>(stringBuilder, encoder, (expression as LambdaExpression).Body, entity);
            }
            else
                throw new NotSupportedException("不支持的表达式，无法导出字段列表。");
        }

        /// <summary>
        /// 生成赋值语句
        /// </summary>
        /// <param name="stringBuilder"></param>
        /// <param name="encoder"></param>
        /// <param name="fieldName"></param>
        /// <param name="entity"></param>
        /// <param name="expression"></param>
        private void BuildSetValue(StringBuilder stringBuilder, 
            DmlEncoder encoder, string fieldName, 
            object entity, Expression expression)
        {
            stringBuilder.Append(fieldName);
            stringBuilder.Append('=');

            object o = ((LambdaExpression)expression).Compile().DynamicInvoke(entity);
            stringBuilder.Append(encoder.EncodeValue(o));
        }

        private void BuildSetValue<TElement>(StringBuilder stringBuilder, DmlEncoder encoder,
            string fieldName, TElement entity, string propertyName)
        {
            stringBuilder.Append(fieldName);
            stringBuilder.Append('=');

            Type entityType = typeof(TElement);
            object o = entityType.GetProperty(propertyName).GetValue(entity, null);
            stringBuilder.Append(encoder.EncodeValue(o));

        }

        internal override void AddExpression(string methodName, System.Linq.Expressions.Expression expression)
        {
            if (methodName == "Select")
            {
                selectClause = (expression as UnaryExpression).Operand as LambdaExpression;
            }
            else if (methodName == "Where")
            {
                if (query != null)
                    query.WhereClause.Add(((expression as UnaryExpression).Operand as LambdaExpression).Body);
                else
                    WhereClause.Add(((expression as UnaryExpression).Operand as LambdaExpression).Body);
            }
            else
                throw new NotSupportedException(string.Format("不支持的方法调用：{0}", methodName));
        }

        internal override Modifiable Clone<TElement>()
        {
            Updatable<TElement> modifier = new Updatable<TElement>();
            modifier.Provider = this.Provider;

            return modifier;
        }

        internal override Modifiable Clone()
        {
            return Clone<T>();
        }

        public override Type ElementType
        {
            get { return typeof(T); }
        }

        #region IEnumerable<T> 成员

        public new IEnumerator<T> GetEnumerator()
        {
            return (IEnumerator<T>)GetEnumerator();
        }

        #endregion
    }
}
